package cn.kivensoft.util;

import java.lang.ref.WeakReference;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.function.Supplier;


/** 对象池，采用弱引用方式保存使用过的对象，减少创建对象的次数，降低内存占用率
 * 使用方法:
 *     static WeakPool<T> x = new WeakPool<>(() -> new T());
 *     PoolItem<A> item = x.get();
 *     A a = item.get();
 *     item.recycle();
 * @author kiven lee
 * @version 1.0
 * @since 2015-09-27
 */
final public class WeakPool<T> {
	private static final int SLEEP_TIME = 100; // 遭遇线程竞争时休眠时间，纳秒为单位(CPU平均执行一条指令为4纳秒)
	// 无锁非阻塞堆栈头部指针
	private WeakReference<Item> head = null;
	// head修改标志, 修改head前要将标志从0变1, 修改后变为0, 用于多线程同步
	private static final ReentrantLock lockHead = new ReentrantLock();

	// 缓存对象生产工厂, 通过回调函数生成新的对象实例
	private final Supplier<T> objFactory;
	// 缓存对象回收工厂, 当调用缓存对象的recycle函数时调用, 用于自定义清理
	private final Consumer<T> recycleFunc;

	public WeakPool(Supplier<T> objFactory) {
		this(objFactory, null);
	}

	public WeakPool(Supplier<T> objFactory, Consumer<T> recycleFunc) {
		this.objFactory = objFactory;
		this.recycleFunc = recycleFunc;
	}

	/** 获取缓存中的对象实例, 缓存没有则新建一个实例返回 */
	public Item get() {
		return pop();
	}

	/** 清除缓存中的所有实例 */
	public void clear() {
		lockHead.lock();
		head = null;
		lockHead.unlock();
	}

	// 无锁非阻塞弹出栈顶元素
	private Item pop() {
		Item item = null;

		if (head != null) {
			lockHead.lock();
			if (head != null) {
				item = head.get();
				head = item != null ? item.next : null;
			}
			lockHead.unlock();
		}

		// 返回对象脱离列表控制
		if (item != null) {
			item.next = null;
			item.recycled = false;
		} else {
			item = new Item(objFactory.get());
		}

		return item;
	}

	// 无锁非阻塞元素压入栈顶
	private void push(Item item) {
		// next不为空, 可能是用户代码重复入栈, 忽略该操作
		if (item.next != null || item.recycled)
			throw new RuntimeException("WeakPool recycle dup");

		// 回调自定义对象清理函数, 进行回收前的处理
		if (recycleFunc != null)
			recycleFunc.accept(item.value);
		item.recycled = true;

		lockHead.lock();
		item.next = head;
		head = item.self;
		lockHead.unlock();
	}

	public class Item {
		private final T value;
		private final WeakReference<Item> self;
		private volatile WeakReference<Item> next;
		private volatile boolean recycled;

		public boolean getRecycled() {
			return recycled;
		}

		public Item(T value) {
			this.value = value;
			this.self = new WeakReference<>(this);
			next = null;
			recycled = false;
		}

		public T getValue() {
			return value;
		}

		public void recycle() {
			WeakPool.this.push(this);
		}

	}

}
